#ifndef RAD_PACKET_H
#define RAD_PACKET_H

typedef struct fr_packet_dst2id_t
{
    fr_ipaddr_t	dst_ipaddr;
    int		dst_port;
    uint32_t	id[1];	/* really id[256] */
} fr_packet_dst2id_t;
typedef struct fr_packet_socket_t
{
    int		sockfd;

    int		num_outgoing;

    int		offset;	/* 0..31 */
    int		inaddr_any;
    fr_ipaddr_t	ipaddr;
    int		port;
} fr_packet_socket_t;
struct fr_packet_list_t
{
    fr_hash_table_t *ht;

    fr_hash_table_t *dst2id_ht;

    int		alloc_id;
    int		num_outgoing;

    uint32_t mask;
    int last_recv;
    fr_packet_socket_t sockets[32];
};

typedef boost::shared_ptr<class rad_packet> rad_packet_ptr;
typedef boost::function<void (rad_packet_ptr &)> rad_callback_t;

class rad_packet : public boost::enable_shared_from_this<rad_packet>
{
public:
	typedef enum 
	{
		AUTH_TIMEOUT = -1,
		ACCOUNT_TIMEOUT = -2,
		AUTH_SUCCESS = 2,
		AUTH_REJECT = 3,
		ACCOUNT_SUCCESS = 5,
	} e_reply_code;
	typedef enum {
		PACKET_AUTH,
		PACKET_ACCOUNT
	} e_packet_type;
	typedef enum {
		ACCT_START,
		ACCT_STOP,
		ACCT_ALIVE
	} e_account_type;

	rad_packet(e_packet_type type, rad_callback_t &call_back) :
		request_(NULL), reply_(NULL), tries_(0), max_tries_(RAD_RESEND_TIME), call_back_(call_back), wait_reply_deadline_(io_service_)
		  ,server_(NULL), max_tried_servers_(RAD_RESEND_SERVER)
	{
		request_ = rad_alloc(1);
		if (!request_)
		{	return;	}
		request_->src_port = 0;
		request_->sockfd = -1;	
		switch (type)
		{
		case PACKET_AUTH:
			request_->dst_port = PW_AUTH_UDP_PORT;
			request_->code = PW_AUTHENTICATION_REQUEST;
			break;
		case PACKET_ACCOUNT:
			request_->dst_port = PW_ACCT_UDP_PORT;
			request_->code = PW_ACCOUNTING_REQUEST;			
			break;
		}
	}
	void set_test()
	{
		pairadd(&request_->vps, pairmake("Veno-Packet-Type", "Veno-Packet-Test", 11));
	}
	// Auth packet
	void set_auth(const char *name, const char *pass)
	{
		pairadd(&request_->vps, pairmake("User-Name", name, 11));
		pairadd(&request_->vps, pairmake("User-Password", pass, 11));
		pairadd(&request_->vps, pairmake("Veno-Packet-Type", "Veno-Packet-ForceOff", 11));
	}
	// Account packet
	void set_account( e_account_type acct_type, const char *name, const char *ip, const unsigned char *session_tx)
			//unsigned int total_down_traffic, unsigned int total_up_traffic, const char *reason, const char *kick_time)
	{		
		string status;
		switch (acct_type)
		{
		case ACCT_START:
			status = "Start";
			break;
		case ACCT_STOP:
			status = "Stop";
			break;
		case ACCT_ALIVE:
			status = "Alive";
			break;
		}
		config_file *cf = config_file::instance;
		if (cf->contain("local_ipv4_from_remote"))
			pairadd(&request_->vps, pairmake("NAS-IP-Address", cf->get_value("local_ipv4_from_remote").c_str(), 11));
		else if (cf->contain("local_ipv4"))
			pairadd(&request_->vps, pairmake("NAS-IP-Address", cf->get_value("local_ipv4").c_str(), 11));
		pairadd(&request_->vps, pairmake("User-Name", name, 11));		
		pairadd(&request_->vps, pairmake("Calling-Station-Id", ip, 11));
		pairadd(&request_->vps, pairmake("Acct-Status-Type", status.c_str(), 11));
		pairadd(&request_->vps, pairmake("Acct-Session-Id", (char *)session_tx, 11));
	}
	void set_version(string version)
	{
		pairadd(&request_->vps, pairmake("Connect-Info", version.c_str(), 11));	
	}
	void set_terminate(string reason, int kick_time = 0)
	{		
		if (reason == "Admin-Reset" ||	reason == "Host-Request")
		{
			if (kick_time > 0) 
			{
				string skick_time = boost::lexical_cast<string>(kick_time);
				pairadd(&request_->vps, pairmake("Session-Timeout", skick_time.c_str(), 11));
			}
		}
		pairadd(&request_->vps, pairmake("Acct-Terminate-Cause", reason.c_str(), 11));
	}
	void set_traffic(unsigned int total_down_traffic, unsigned int total_up_traffic)
	{
		char temp[100] ={0};
		unsigned int t;
		t = total_down_traffic >> 22;  //giga words
		sprintf(temp, "%u", t);
		pairadd(&request_->vps, pairmake("Acct-Input-Gigawords", temp, 11));
		memset(temp, 0 , sizeof(temp));
		t = (total_down_traffic - (t << 22)) << 10; 
		sprintf(temp, "%u", t);
		pairadd(&request_->vps, pairmake("Acct-Input-Octets", temp, 11));
		memset(temp, 0 , sizeof(temp));

		t = total_down_traffic >> 22;  //giga words
		sprintf(temp, "%u", t);
		pairadd(&request_->vps, pairmake("Acct-Output-Gigawords", temp, 11));
		memset(temp, 0 , sizeof(temp));
		t = (total_down_traffic - (t << 22)) << 10; 
		sprintf(temp, "%u", t);
		pairadd(&request_->vps, pairmake("Acct-Output-Octets", temp, 11));
		memset(temp, 0 , sizeof(temp));					
			
	}
	~rad_packet()
	{
		cout << "packet free" << endl;
		if (request_) rad_free(&request_);
		//if (radclient->reply) rad_free(&radclient->reply);
	}
	void set_try_times(int resend)
	{
		max_tries_ = resend;
	}
	void set_try_servers(int server)
	{
		max_tried_servers_ = server;
	}
	void start_timer()
	{
		wait_reply_deadline_.expires_from_now(boost::posix_time::seconds(RAD_RESEND_WAIT_SECOND));
		// authentication time out
		wait_reply_deadline_.async_wait(
			boost::bind(&rad_packet::check_deadline,
			shared_from_this()));
	}
	void cancel_timer()
	{
		wait_reply_deadline_.cancel();
	}
	void check_deadline();
	// class this will be deleted after this call
	void parse_reply(RADIUS_PACKET *reply)
	{
		VALUE_PAIR *v, *vp = reply->vps;
		string key, value;
		char temp[1024];

		rad_reply_code_ = reply->code;
		for (v = vp; v; v = v->next)
		{
			if (!v->name || !*v->name)
			{
				if (!vp_print_name(temp, sizeof(temp), v->attribute))
				{
					return;
				}
				key = string(temp);
			}
			else
				key = string(v->name);
			vp_prints_value(temp, sizeof(temp), v, 1);
			value = string(temp);
			reply_pairs_[key] = value;
			cout << key << " " << value << endl;
		}
		rad_packet_ptr p = shared_from_this();
		if (call_back_ != NULL) call_back_(p);
	}
	bool reply_contain(const char *key)
	{		
		return reply_pairs_.find(key) != reply_pairs_.end();
	}
	string &reply_value(const char *key)
	{
		return reply_pairs_[key];
	}
		
    RADIUS_PACKET	*request_;
    RADIUS_PACKET	*reply_;
    int		tries_;
	int		max_tries_;
	int		rad_reply_code_;
	map<string, string> reply_pairs_;

	rad_callback_t call_back_;

	deadline_timer wait_reply_deadline_;

	class rad_server *server_;
	set <class rad_server *> tried_servers_;
	int		max_tried_servers_;

	static boost::asio::io_service& io_service_;
private:

	
};













#endif